/**
 * Project: pws-formula
 * Created Date: Thursday, May 3rd 2018, 12:19:08 pm
 * Author: Thomas.Li
 * E-Mail: leeyinghui@hotmail.com
 * Intro: Buffer operation methods
 * -----
 * Last Modified: Tue May 29 2018
 * Modified By: Thomas.li
 * -----
 * Copyright (c) 2018 pareact
 * ------------------------------------
 * Always bet on Javascript!
 */

const getCRC = require('./crc').getCRC;

let buf; //temp buffer

/**
 * get the Hex value of DEC
 * @param {number} start
 * @param {number} [length=2]
 * @returns
 */
function hx(start, length = 2) {
  if (length > 4 || length < 0) {
    throw new RangeError('Out of Index Range');
  }
  return buf.readUIntBE(start, length);
}

/**
 * get the Hex value of DEC (can read negative number)
 * @param {number} start
 * @param {number} [length=2]
 * @returns
 */
function hxu(start, length = 2) {
  if (length > 4 || length < 0) {
    throw new RangeError('Out of Index Range');
  }
  return buf.readIntBE(start, length);
}

/**
 * get the float value from 4 byte buffer
 * @param {number} start
 */
function hp(start) {
  if (start < 0 || start > buf.length - 4) {
    throw new RangeError('Out of the range');
  }
  let result = [...Array(4).keys()].map(i =>
    buf[start + i].toString(2).padStart(8, '0')
  );
  result = result.join('');
  let reg = /^[0-1]*$/;
  if (!reg.test(result)) throw new TypeError('The value is invalid');
  let mark = result[0] == '1' ? -1 : 1;
  let eMark = parseInt(result.slice(1, 9), 2);
  let mMark = parseInt(result.slice(9), 2);
  let r1 = 2 ** (eMark - 127);
  let r2 = mark * r1 * mMark * 2 ** -23;
  return Math.round((r1 + r2) * 100) / 100;
}

/**
 * get the float value by ascii code (00 replaced to 30)
 * @param {number} start
 * @param {number} length
 * @returns
 * @memberof HexFormula
 */
function hc(start, length) {
  let temp = buf.slice(start, start + length);
  if (temp.length < length) {
    throw new RangeError('Out of Index Range');
  }
  let result = temp.map(x => (x == 0 ? 48 : x));
  if (result.some(x => x == 47 || x < 45 || x > 57)) {
    throw new TypeError('The value is invalid');
  } else {
    return parseFloat(result.toString());
  }
}

/**
 * get the BCD Value
 * @param {number} start
 * @param {number} length
 * @returns
 * @memberof HexFormula
 */
function ht(start, length) {
  let temp = buf.slice(start, start + length);
  if (temp.length < length) {
    throw new RangeError('Out of Index Range');
  }
  let result = temp.toString('hex');
  let reg = /^[0-9]*$/;
  if (reg.test(result)) {
    return parseInt(result.toString());
  } else {
    throw new TypeError('The value is invalid');
  }
}

/**
 * get the bit value from index and order
 *
 * @param {mumber} index
 * @param {number} order 0-7  from right to left  0xf0 = 0b11110000 (7-6-5-4-3-2-1-0)
 * @returns
 * @memberof HexFormula
 */
function hb(index, order) {
  if (order >= 8 || order < 0) {
    throw new TypeError('The value must be 0-7', 'hex.js', 40);
  }
  let temp = buf.readUInt8(index);
  return (temp & (2 ** order)) >>> order;
}

/**
 * Trinocular calculate
 * @param {string} condition
 * @param {string} a
 * @param {string} b
 * @returns
 */
function pif(condition, a, b) {
  return eval(condition) ? eval(a) : eval(b);
}

/**
 * outer method to call the inner formula by eval(str)
 * @param {Buffer} bf
 * @param {string} formulas
 * @returns
 */
function getBuffValue(bf, formulas) {
  buf = bf;
  return formulas.map(x => {
    try {
      let result = eval(x);
      return { success: true, data: result };
    } catch (e) {
      return { success: false, data: e };
    }
  });
}

/**
 * check if the buffer is valid by sort (crc,sum)
 * @param {Buffer} bf
 * @param {number} sort
 * @returns
 */
function verifyBuffer(bf, sort) {
  buf = bf;
  if (buf.length < 3) {
    return { success: false, data: 'The buffer is too short!' };
  }
  try {
    //crc check
    if (sort == 1) {
      let originalValue = buf.readUInt16LE(buf.length - 2);
      let tempBuf = buf.slice(0, buf.length - 2);
      let checkValue = getCRC(tempBuf);
      return {
        success: originalValue == checkValue,
        data: `${originalValue}-${checkValue}`
      };
    }
    //sum
    else if (sort == 3) {
      let originalValue = buf.readUInt8(buf.length - 1);
      let checkValue = buf.reduce((a, b) => a + b) - originalValue;
      return {
        success: checkValue % 256 == originalValue,
        data: `${originalValue}-${checkValue}`
      };
    } else if (sort == 0) {
      return { success: true, data: `no need to valid data` };
    } else {
      return { success: false, data: 'Sort value is invalid!' };
    }
  } catch (e) {
    return { success: false, data: e };
  }
}

/**
 * get the verified result from original buffer and sort(crc, sum)
 * @param {Buffer} bf
 * @param {number} sort
 * @returns
 */
function getFullBuffer(bf, sort) {
  buf = bf;
  if (buf.length < 3) {
    return { success: false, data: 'The buffer is too short!' };
  }
  try {
    if (sort == 1) {
      let checkValue = getCRC(buf);
      let tempBuf = Buffer.alloc(2);
      tempBuf.writeUInt16LE(checkValue, 0);
      return {
        success: true,
        data: Buffer.concat([buf, tempBuf], buf.length + 2)
      };
    } else if (sort == 3) {
      let checkValue = buf.reduce((a, b) => a + b) % 256;
      let tempBuf = Buffer.alloc(1);
      tempBuf.writeUInt8(checkValue, 0);
      return {
        success: true,
        data: Buffer.concat([buf, tempBuf], buf.length + 1)
      };
    } else {
      return { success: false, data: 'Sort value is invalid!' };
    }
  } catch (e) {
    return { success: false, data: e };
  }
}

module.exports = {
  getBuffValue: getBuffValue,
  verifyBuffer: verifyBuffer,
  getFullBuffer: getFullBuffer
};
